#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"

static int gcap = 5;

template <typename T>
class RingQueue
{
public:
    // 构造
    RingQueue(int cap = gcap)
        : _cap(cap), _ring_queue(cap), _space_sem(cap), _data_sem(0), _p_step(0), _c_step(0)
    {
    }

    // 放入数据
    void Enqueue(const T &in)
    {
        _space_sem.P();
        {
            //为了支持多个生产者同时生产就需要加锁
            LockGuard lockguard(&_p_lock); // 在作用域中自动上锁、解锁
            //  将数据放入
            _ring_queue[_p_step++] = in;
            _p_step %= _cap; // 维持环形的状态
        }
        _data_sem.V();
    }

    // 消费者取出数据
    void Pop(T *out) // 输出型参数
    {
        _data_sem.P();
        { 
            //为了支持多个消费者同时消费也需要加锁
            LockGuard lockguard(&_p_lock);
            // 拿出数据
            *out = _ring_queue[_c_step++];
            _c_step %= _cap;
        }
        _space_sem.V();
    }
    // 析构
    ~RingQueue() // 内置类型、自定义类型可以不用写构造函数
    {
    }

private:
    // 底层用数组+模运算就可以实现带环队列
    std::vector<int> _ring_queue;
    int _cap; // 容量

    // 两个信号量分别来记录数据个数和空间个数
    Sem _space_sem;
    Sem _data_sem;

    // 分别记录生产和消费的下标
    int _p_step;
    int _c_step;

    // 放数据与写数据均需要一把锁，才能更好地实现并行
    Mutex _p_lock;
    Mutex _c_lock;
};